"use strict";
import H from "../../parts/Globals.js";
import "../../parts/Utilities.js";

var Annotation = H.Annotation,
  MockPoint = Annotation.MockPoint,
  Tunnel = Annotation.types.tunnel;

var createPathDGenerator = function (retracementIndex, isBackground) {
  return function () {
    var annotation = this.annotation,
      leftTop = this.anchor(
        annotation.startRetracements[retracementIndex]
      ).absolutePosition,
      rightTop = this.anchor(
        annotation.endRetracements[retracementIndex]
      ).absolutePosition,
      d = [
        "M",
        Math.round(leftTop.x),
        Math.round(leftTop.y),
        "L",
        Math.round(rightTop.x),
        Math.round(rightTop.y),
      ],
      rightBottom,
      leftBottom;

    if (isBackground) {
      rightBottom = this.anchor(
        annotation.endRetracements[retracementIndex - 1]
      ).absolutePosition;

      leftBottom = this.anchor(
        annotation.startRetracements[retracementIndex - 1]
      ).absolutePosition;

      d.push(
        "L",
        Math.round(rightBottom.x),
        Math.round(rightBottom.y),
        "L",
        Math.round(leftBottom.x),
        Math.round(leftBottom.y)
      );
    }

    return d;
  };
};

/**
 * @class
 * @extends Annotation.Tunnel
 * @memberOf Annotation
 **/
function Fibonacci() {
  this.startRetracements = [];
  this.endRetracements = [];

  Tunnel.apply(this, arguments);
}

Fibonacci.levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1];

H.extendAnnotation(
  Fibonacci,
  Tunnel,
  /** @lends Annotation.Fibonacci# */
  {
    linkPoints: function () {
      Tunnel.prototype.linkPoints.call(this);

      this.linkRetracementsPoints();
    },

    linkRetracementsPoints: function () {
      var points = this.points,
        startDiff = points[0].y - points[3].y,
        endDiff = points[1].y - points[2].y,
        startX = points[0].x,
        endX = points[1].x;

      Fibonacci.levels.forEach(function (level, i) {
        var startRetracement = points[0].y - startDiff * level,
          endRetracement = points[1].y - endDiff * level;

        this.linkRetracementPoint(
          i,
          startX,
          startRetracement,
          this.startRetracements
        );

        this.linkRetracementPoint(
          i,
          endX,
          endRetracement,
          this.endRetracements
        );
      }, this);
    },

    linkRetracementPoint: function (pointIndex, x, y, retracements) {
      var point = retracements[pointIndex],
        typeOptions = this.options.typeOptions;

      if (!point) {
        retracements[pointIndex] = new MockPoint(this.chart, this, {
          x: x,
          y: y,
          xAxis: typeOptions.xAxis,
          yAxis: typeOptions.yAxis,
        });
      } else {
        point.options.x = x;
        point.options.y = y;

        point.refresh();
      }
    },

    addShapes: function () {
      Fibonacci.levels.forEach(function (level, i) {
        this.initShape(
          {
            type: "path",
            d: createPathDGenerator(i),
          },
          false
        );

        if (i > 0) {
          this.initShape({
            type: "path",
            fill: this.options.typeOptions.backgroundColors[i - 1],
            strokeWidth: 0,
            d: createPathDGenerator(i, true),
          });
        }
      }, this);
    },

    addLabels: function () {
      Fibonacci.levels.forEach(function (level, i) {
        var options = this.options.typeOptions,
          label = this.initLabel(
            H.merge(options.labels[i], {
              point: function (target) {
                var point = MockPoint.pointToOptions(
                  target.annotation.startRetracements[i]
                );

                return point;
              },
              text: level.toString(),
            })
          );

        options.labels[i] = label.options;
      }, this);
    },
  },

  /**
   * A fibonacci annotation.
   *
   * @sample highcharts/annotations-advanced/fibonacci/
   *         Fibonacci
   *
   * @extends      annotations.crookedLine
   * @product      highstock
   * @optionparent annotations.fibonacci
   */
  {
    typeOptions: {
      /**
       * The height of the fibonacci in terms of yAxis.
       */
      height: 2,

      /**
       * An array of background colors:
       * Default to:
       * ```
       * [
       * 'rgba(130, 170, 255, 0.4)',
       * 'rgba(139, 191, 216, 0.4)',
       * 'rgba(150, 216, 192, 0.4)',
       * 'rgba(156, 229, 161, 0.4)',
       * 'rgba(162, 241, 130, 0.4)',
       * 'rgba(169, 255, 101, 0.4)'
       * ]
       * ```
       */
      backgroundColors: [
        "rgba(130, 170, 255, 0.4)",
        "rgba(139, 191, 216, 0.4)",
        "rgba(150, 216, 192, 0.4)",
        "rgba(156, 229, 161, 0.4)",
        "rgba(162, 241, 130, 0.4)",
        "rgba(169, 255, 101, 0.4)",
      ],

      /**
       * The color of line.
       */
      lineColor: "grey",

      /**
       * An array of colors for the lines.
       */
      lineColors: [],

      /**
       * An array with options for the labels.
       *
       * @type      {Array<object>}
       * @extends   annotations.crookedLine.labelOptions
       * @apioption annotations.fibonacci.typeOptions.labels
       */
      labels: [],
    },

    labelOptions: {
      allowOverlap: true,
      align: "right",
      backgroundColor: "none",
      borderWidth: 0,
      crop: false,
      overflow: "none",
      shape: "rect",
      style: {
        color: "grey",
      },
      verticalAlign: "middle",
      y: 0,
    },
  }
);

Annotation.types.fibonacci = Fibonacci;

export default Fibonacci;
